/*
*    Phantom v2.0 GUI Automation Software
*    Copyright (C) 2009  John C. Van Arsdall (john@phantomtest.com)
*
*    This program is free software; you can redistribute it and/or modify
*    it under the terms of the GNU General Public License as published by
*    the Free Software Foundation; either version 2 of the License, or
*    (at your option) any later version.
*
*    This program is distributed in the hope that it will be useful,
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*    GNU General Public License for more details.
*
*    You should have received a copy of the GNU General Public License along
*    with this program; if not, write to the Free Software Foundation, Inc.,
*    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
// Variable.cpp: implementation of the CVariable class.
//
//////////////////////////////////////////////////////////////////////

#include "ssi_variable.h"
#include "ssi_data.h"
#include "ssi_interpreter.h"
#include "ssi_variable.h"
#include "ssi_internals.h"
#include "ssi_dataarray.h"


//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CVariable::CVariable()
{
	// 1 SSI_DISPLAY_FILE_NEW
  array = new CDataArray;
  array->Increment();

  Type = SSI_NULL;
  WorkingNode = SSI_NULL;
	Access = ACCESS_GLOBAL;
}

CVariable::~CVariable()
{
  SSI_INT64 ctr = array->Decrement();
  if(ctr <= 0) 
  {
		// 1 SSI_DISPLAY_FILE_DEL
		//array->
    //zap(array);
		if(array){
			delete array;
			array = 0;
		}
  }
	del_array_node(WorkingNode);
	WorkingNode = SSI_NULL;
}

SSI_BOOL CVariable::operator ==(const CVariable &Other)
{
  if(Other.Type != dUNKNOWN)
  {
    if(!(Other.Type == Type)) return 0;
  }
  if(Other.Name == Name) return 1;

  return 0;
}

CVariable& CVariable::operator =(CVariable &Other)
{
  Name = Other.Name;
  Type = Other.Type;
	Access = Other.Access;
  if(WorkingNode) del_array_node(WorkingNode);
  WorkingNode = copy_array_node(Other.WorkingNode);
  ResetArray(Other.array);

  return *this;
}

CVariable& CVariable::operator<<(CVariable &Var)
{
  // Do something here regarding the index

  //CopyAllData(Var);

  ResetArray(Var.array);
  return *this;
}

CVariable& CVariable::operator<<(CData* Data /*DataArray &In*/)
{
  // Do something here regarding the index
  //CopyAllData(In);
  //copyData(Data);
  return *this;
}

CVariable::operator CData*()
{
  return (CData*)&GetWorkingData();
}

SSI_INT64 CVariable::GetType()
{
  return Type;  
}

String CVariable::GetTypeString()
{
  return CInterpreter::GetVariableString(Type);  
}

// This needs to accept a dimension node
CStatus CVariable::SetData(const String& sData, DimNode Dim)
{
  return array->AddData(sData, Dim);
}

// This needs to accept a dimension node
CStatus CVariable::SetData(CData *aData, DimNode Dim)
{
	CData* newData = Factory_MakeData(aData->Type);  //aData->MakeData(
	if(!newData) return STAT_FAIL;
	*newData = *aData;
	// This will get deleted later
	// Need to have the above code because if a reference is passed,
	//  it will wrongly deleted.  Perhaps find a better way later
  return array->AddData(newData, Dim);
}

CData* CVariable::NewData()
{
  return CData::NewData(Type);
}


// This needs to be a dimension node
CData& CVariable::Get(DimNode Dim)
{
  if(Dim == SSI_NULL)
  {
		DimensionT aDim;
		aDim.Dim = 1;
		aDim.Next = 0;
		return array->GetData(&aDim);
	}else{
		return array->GetData(Dim);
	}
}

// This needs to be a dimension node
CData* CVariable::Get(SSI_INT64 Ind)
{
  return array->GetData(Ind);
}


CVariable::CVariable(const CVariable &Other)
: Flags(0)
{
	//if(array){
	//	zap(array);  // should never get here...
	//}
  // need to do this
	// 1 SSI_DISPLAY_FILE_NEW
  array = new CDataArray;
  array->Increment();

  Name = Other.Name;
  Type = Other.Type;
	Access = Other.Access;

  // WorkingNode will always be 0 here
  //if(WorkingNode) del_array_node(WorkingNode);
  WorkingNode = copy_array_node(Other.WorkingNode);

  ResetArray(Other.array);
  //CopyAllData(Other);
}

void CVariable::copyData(CData *In)
{
  int i = 10;
}

SSI_INT64 CVariable::TypeFromString(const String& S)
{
  String Temp = S;
  Temp.Trim();
	CData* aData = Factory_DataFromString(Temp.Str());
	if(!aData) return SSI_NULL;
	SSI_INT64 Ret = aData->GetType();
	delete aData;
	return Ret;

//Will I need to redo this?  Who knows?
	//if(Temp.CheckEndQuotes()) return CDataString::GetRegisteredType();//dSTRING;
  //if((Temp == sTRUE) || (Temp == sFALSE)) return CDataBool::GetRegisteredType();
  //if(Temp.CheckChars(FLAG_INT)) return CDataInt::GetRegisteredType();
  //if(Temp.CheckChars(FLAG_REAL)) return CDataReal::GetRegisteredType();

  return SSI_NULL;
}

// MUST BE REDONE
// Must be changed, and compatibility checks done from data
/*
SSI_BOOL CVariable::CheckCompatibility(SSI_INT64 Type1, SSI_INT64 Type2)
{
  if(Type1 == Type2) return 1;
  if(Type1 == CDataReal::GetRegisteredType())
  {
    if(Type2 == CDataInt::GetRegisteredType()) return 1;
  }
  if(Type1 == CDataBool::GetRegisteredType())
  {
    if(Type2 == CDataInt::GetRegisteredType())
    {
      return 1;
    }
  }
  return 0;
}
*/

CVariable::operator int()
{
  if(Type == SSI_NULL) return 0;
  return 1;
}


void CVariable::CopyAllData(CVariable& In)
{
  array->Type = In.array->Type;
  if(array->GetAccessCtr() == 1){
		// Continue to use the same array
  }else{
    array->Decrement();
		// 1 SSI_DISPLAY_FILE_NEW
    array = new CDataArray;
		array->Increment();
  }
  *array = *(In.array);  // Does this increase the access counter?  No...
}

//DEL DimNode CVariable::InitializeDimensions()
//DEL {
//DEL   array.clear();
//DEL   DimNode DefaultDim = new DimensionT;
//DEL   DefaultDim->Dim = 1;
//DEL   DefaultDim->Next = SSI_NULL;
//DEL   return DefaultDim;
//DEL }

void CVariable::SetType(SSI_INT64 NewType)
{
  Type = NewType;
  
  if(array->GetAccessCtr() == 1){
		// 1 SSI_DISPLAY_FILE_DEL
    zap(array);
		// 1 SSI_DISPLAY_FILE_NEW
    array = new CDataArray;
  }else{
    array->Decrement();
		// 1 SSI_DISPLAY_FILE_NEW
    array = new CDataArray;
  }
  array->Increment();
  array->Type = Type;

}

SSI_INT64 CVariable::GetNumElems()
{
  return array->GetNumElems();
}

void CVariable::SetWorkingNode(DimNode New)
{
	del_array_node(WorkingNode);
  WorkingNode = New;
}

DimNode CVariable::GetWorkingNode()
{
  return WorkingNode;
}

CStatus CVariable::SetWorkingData(CData *In)
{
  return SetData(In, WorkingNode);
}

CData& CVariable::GetWorkingData()
{
  return Get(WorkingNode);
  
}

CVariable CVariable::GetWorkingVariable()
{
  CVariable Ret;
  Ret.SetType(GetType());
  if(WorkingNode){
		CData* aData = Factory_MakeData(Ret.GetType());
		*aData = Get(WorkingNode);
		Ret.SetData(aData);
		// Dont need to delete, because gets deleted through SetData
    // Um... yes we do...
    delete aData;
    //Ret.SetData(&Get(WorkingNode));
  }else{
    Ret = *this;
  }
  return Ret;
}

CVariable& CVariable::CopyVariable(CVariable &In)
{
  Name = In.Name;
  Type = In.Type;
  if(WorkingNode) del_array_node(WorkingNode);
	WorkingNode = copy_array_node(In.WorkingNode);
  CopyAllData(In);

  return *this;
}

void CVariable::ResetArray(CDataArray *New)
{
  if(array->GetAccessCtr() == 1){
		// 1 SSI_DISPLAY_FILE_DEL
    zap(array);
  }else{
    array->Decrement();
  }

	if(!New){
		// 1 SSI_DISPLAY_FILE_NEW
		array = new CDataArray;
		array->Type = Type;
	}else{
		array = New;
	}
  array->Increment();
}

CData* CVariable::GetNextData(DimNode& Dim, SSI_BOOL& EOL, SSI_BOOL bReset, SSI_BOOL bIncludeNULLs)
{
	return array->GetNextData(Dim, EOL, bReset, bIncludeNULLs);
}

DimNode CVariable::GetDims(void)
{
	return array->GetDims();
}

bool CVariable::operator<(const CVariable& Other) const
{
  String myName = Name;
  String theirName = Other.Name;

  myName.ToUpper();
  theirName.ToUpper();

  return myName < theirName;
}


